/**
 * 题1：手写 Object.create()
 * 思路：把转入的对象作为构造函数原型，并且返回这个构造函数的实例
 */
function create(o) {
  function F() {}
  F.prototype = o
  return new F()
}

/**
 * 题2：手写 instanceof 判断构造函数的原型是否出现在对象的原型链上
 * 思路：遍历对象的原型链，判断构造函数的原型是否和对象的原型相等
 */
function myInstanceof(obj, Fn) {
  // 1.基础数据类型直接返回 false
  if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) {
    return false
  }
  // 2.获取构造函数的原型
  let prototype = Fn.prototype
  // 3.获取对象的原型，也可使用 Objcet.getPrototypeOf(obj)，__proto__ 兼容性更好
  let objPro = obj.__proto__
  // 4.objPro 不为空时继续循环，找到返回 true，没找到把 objPro 设置为 obj 原型的原型
  while(objPro !== null) {
    if (prototype === objPro) return true
    objPro = objPro.__proto__
  }
  // 5.objPro 为 null 表示查找到最顶层没找到，返回 false
  return false
}

/**
 * 题3：手写 new 创建一个构造函数的实例对象
 * new 做的事情：1) 创建一个空对象
 * 2) 空对象的原型属性设置为构造函数的原型 prototype
 * 3) 构造函数的 this 指向这个对象，执行构造函数的代码(增强这个对象，给对象添加属性)
 * 4) 构造函数是否返回了一个引用类型的对象，是的话 new 返回该对象，否则返回新创建的这个对象
 */
function myNew(Fn, ...args) {
  // 1.判断第一个参数类型，必须是函数
  if (typeof Fn !== 'function') {
    throw new TypeError('type error')
  }
  // 2.以构造函数的原型创建一个新对象
  let newObj = Object.create(Fn.prototype)
  // 3.执行构造函数的代码
  let result = Fn.call(newObj, args)
  // 4.判断返回值类型
  if (typeof result === 'object' || typeof result === 'function' || typeof result === null) {
    return result
  }
  return newObj
}

/**
 * 题4：手写防抖 每次事件触发则删除原来的定时器，建立新的定时器，从最后一次触发重新开始计时。
 * 示例：王者荣耀回城按钮、输入框 keyup 事件(最后一次键盘弹起三秒后才会打印输入框的值)
 */
function debounce(fn, delay) {
  let timer = null
  return function(...args) {
    let context = this
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(context, args)
    }, delay)
  }
}

/**
 * 题5：手写节流 指定时间间隔内事件只触发一次，经过指定时间之后才能触发下一次，上一次触发后经过指定时间间隔才能开启下一个任务
 * 示例：浏览器窗口resize、 scroll、按钮的 click事件
 */
function throttle(fn, interval) {
  let last = 0
  return function(...args) {
    let now = +new Date()
    let context = this
    // 间隔时间小于指定时间，不做操作，直接 return
    if (now - last < interval) return
    last = now
    fn.apply(context, args)
  }
}